/*
 * Copyright (c) 2002 by David I. Bell
 * Permission is granted to use, distribute, or modify this source,
 * provided that this copyright notice remains intact.
 *
 * Utility routines.
 */

#include "sash.h"
#include "eval-upmc.h" /* Added by jean-luc.amitousa-mankoy@hotmail.fr 
			  and boris.de.finance@gmail.com
			  for upmc CL project 2013-2014 */

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <utime.h>


/*
 * A chunk of data.
 * Chunks contain data which is allocated as needed, but which is
 * not freed until all of the data needs freeing, such as at
 * the beginning of the next command.
 */
typedef	struct	chunk	CHUNK;
#define	CHUNK_INIT_SIZE	4

struct	chunk
{
  CHUNK *	next;
  char	data[CHUNK_INIT_SIZE];	/* actually of varying length */
};


static	CHUNK *	chunkList;



/*
 * Return the standard ls-like mode string from a file mode.
 * This is static and so is overwritten on each call.
 */
const char *
modeString(int mode)
{
  static	char	buf[12];

  strcpy(buf, "----------");

  /*
   * Fill in the file type.
   */
  if (S_ISDIR(mode))
    buf[0] = 'd';
  if (S_ISCHR(mode))
    buf[0] = 'c';
  if (S_ISBLK(mode))
    buf[0] = 'b';
  if (S_ISFIFO(mode))
    buf[0] = 'p';
#ifdef	S_ISLNK
  if (S_ISLNK(mode))
    buf[0] = 'l';
#endif
#ifdef	S_ISSOCK
  if (S_ISSOCK(mode))
    buf[0] = 's';
#endif

  /*
   * Now fill in the normal file permissions.
   */
  if (mode & S_IRUSR)
    buf[1] = 'r';
  if (mode & S_IWUSR)
    buf[2] = 'w';
  if (mode & S_IXUSR)
    buf[3] = 'x';
  if (mode & S_IRGRP)
    buf[4] = 'r';
  if (mode & S_IWGRP)
    buf[5] = 'w';
  if (mode & S_IXGRP)
    buf[6] = 'x';
  if (mode & S_IROTH)
    buf[7] = 'r';
  if (mode & S_IWOTH)
    buf[8] = 'w';
  if (mode & S_IXOTH)
    buf[9] = 'x';

  /*
   * Finally fill in magic stuff like suid and sticky text.
   */
  if (mode & S_ISUID)
    buf[3] = ((mode & S_IXUSR) ? 's' : 'S');
  if (mode & S_ISGID)
    buf[6] = ((mode & S_IXGRP) ? 's' : 'S');
  if (mode & S_ISVTX)
    buf[9] = ((mode & S_IXOTH) ? 't' : 'T');

  return buf;
}


/*
 * Get the time string to be used for a file.
 * This is down to the minute for new files, but only the date for old files.
 * The string is returned from a static buffer, and so is overwritten for
 * each call.
 */
const char *
timeString(time_t timeVal)
{
  time_t		now;
  char *		str;
  static	char	buf[26];

  time(&now);

  str = ctime(&timeVal);

  strcpy(buf, &str[4]);
  buf[12] = '\0';

  if ((timeVal > now) || (timeVal < now - 365*24*60*60L))
    {
      strcpy(&buf[7], &str[20]);
      buf[11] = '\0';
    }

  return buf;
}


/*
 * Return TRUE if a fileName is a directory.
 * Nonexistant files return FALSE.
 */
BOOL
isDirectory(const char * name)
{
  struct	stat	statBuf;

  if (stat(name, &statBuf) < 0)
    return FALSE;

  return S_ISDIR(statBuf.st_mode);
}


/*
 * Return TRUE if a filename is a block or character device.
 * Nonexistant files return FALSE.
 */
BOOL
isDevice(const char * name)
{
  struct	stat	statBuf;

  if (stat(name, &statBuf) < 0)
    return FALSE;

  return S_ISBLK(statBuf.st_mode) || S_ISCHR(statBuf.st_mode);
}


/*
 * Copy one file to another, while possibly preserving its modes, times,
 * and modes.  Returns TRUE if successful, or FALSE on a failure with an
 * error message output.  (Failure is not indicted if the attributes cannot
 * be set.)
 */
BOOL
copyFile(
	 const char *	srcName,
	 const char *	destName,
	 BOOL		setModes
	 )
{
  int		rfd;
  int		wfd;
  int		rcc;
  char		buf[BUF_SIZE];
  struct	stat	statBuf1;
  struct	stat	statBuf2;
  struct	utimbuf	times;
	
  if (stat(srcName, &statBuf1) < 0)
    {
      perror(srcName);

      return FALSE;
    }

  if (stat(destName, &statBuf2) < 0)
    {
      statBuf2.st_ino = -1;
      statBuf2.st_dev = -1;
    }

  if ((statBuf1.st_dev == statBuf2.st_dev) &&
      (statBuf1.st_ino == statBuf2.st_ino))
    {
      fprintf(stderr, "Copying file \"%s\" to itself\n", srcName);

      return FALSE;
    }

  rfd = open(srcName, O_RDONLY);

  if (rfd < 0)
    {
      perror(srcName);

      return FALSE;
    }

  wfd = creat(destName, statBuf1.st_mode);

  if (wfd < 0)
    {
      perror(destName);
      close(rfd);

      return FALSE;
    }

  while ((rcc = read(rfd, buf, sizeof(buf))) > 0)
    {
      if (intFlag)
	{
	  close(rfd);
	  close(wfd);

	  return FALSE;
	}

      if (fullWrite(wfd, buf, rcc) < 0)
	goto error_exit;
    }

  if (rcc < 0)
    {
      perror(srcName);
      goto error_exit;
    }

  (void) close(rfd);

  if (close(wfd) < 0)
    {
      perror(destName);

      return FALSE;
    }

  if (setModes)
    {
      (void) chmod(destName, statBuf1.st_mode);

      (void) chown(destName, statBuf1.st_uid, statBuf1.st_gid);

      times.actime = statBuf1.st_atime;
      times.modtime = statBuf1.st_mtime;

      (void) utime(destName, &times);
    }

  return TRUE;


 error_exit:
  close(rfd);
  close(wfd);

  return FALSE;
}


/*
 * Build a path name from the specified directory name and file name.
 * If the directory name is NULL, then the original fileName is returned.
 * The built path is in a static area, and is overwritten for each call.
 */
const char *
buildName(const char * dirName, const char * fileName)
{
  const char *	cp;
  static	char	buf[PATH_LEN];

  if ((dirName == NULL) || (*dirName == '\0'))
    return fileName;

  cp = strrchr(fileName, '/');

  if (cp)
    fileName = cp + 1;

  strcpy(buf, dirName);
  strcat(buf, "/");
  strcat(buf, fileName);

  return buf;
}


/*
 * Expand the wildcards in a fileName wildcard pattern, if any.
 * Returns an argument list with matching fileNames in sorted order.
 * The expanded names are stored in memory chunks which can later all
 * be freed at once.  The returned list is only valid until the next
 * call or until the next command.  Returns zero if the name is not a
 * wildcard, or returns the count of matched files if the name is a
 * wildcard and there was at least one match, or returns -1 if either
 * no fileNames matched or there was an allocation error.
 */
int
expandWildCards(const char * fileNamePattern, const char *** retFileTable)
{
  const char *	last;
  const char *	cp1;
  const char *	cp2;
  const char *	cp3;
  char *		str;
  DIR *		dirp;
  struct dirent *	dp;
  int		dirLen;
  int		newFileTableSize;
  char **		newFileTable;
  char		dirName[PATH_LEN];

  static int	fileCount;
  static int	fileTableSize;
  static char **	fileTable;

  /*
   * Clear the return values until we know their final values.
   */
  fileCount = 0;
  *retFileTable = NULL;

  /*
   * Scan the file name pattern for any wildcard characters.
   */
  cp1 = strchr(fileNamePattern, '*');
  cp2 = strchr(fileNamePattern, '?');
  cp3 = strchr(fileNamePattern, '[');

  /*
   * If there are no wildcard characters then return zero to
   * indicate that there was actually no wildcard pattern.
   */
  if ((cp1 == NULL) && (cp2 == NULL) && (cp3 == NULL))
    return 0;

  /*
   * There are wildcards in the specified filename.
   * Get the last component of the file name.
   */
  last = strrchr(fileNamePattern, '/');

  if (last)
    last++;
  else
    last = fileNamePattern;

  /*
   * If any wildcards were found before the last filename component
   * then return an error.
   */
  if ((cp1 && (cp1 < last)) || (cp2 && (cp2 < last)) ||
      (cp3 && (cp3 < last)))
    {
      fprintf(stderr,
	      "Wildcards only implemented for last file name component\n");

      return -1;
    }

  /*
   * Assume at first that we are scanning the current directory.
   */
  dirName[0] = '.';
  dirName[1] = '\0';

  /*
   * If there was a directory given as part of the file name then
   * copy it and null terminate it.
   */
  if (last != fileNamePattern)
    {
      memcpy(dirName, fileNamePattern, last - fileNamePattern);
      dirName[last - fileNamePattern - 1] = '\0';

      if (dirName[0] == '\0')
	{
	  dirName[0] = '/';
	  dirName[1] = '\0';
	}
    }

  /*
   * Open the directory containing the files to be checked.
   */
  dirp = opendir(dirName);

  if (dirp == NULL)
    {
      perror(dirName);

      return -1;
    }

  /*
   * Prepare the directory name for use in making full path names.
   */
  dirLen = strlen(dirName);

  if (last == fileNamePattern)
    {
      dirLen = 0;
      dirName[0] = '\0';
    }
  else if (dirName[dirLen - 1] != '/')
    {
      dirName[dirLen++] = '/';
      dirName[dirLen] = '\0';
    }

  /*
   * Find all of the files in the directory and check them against
   * the wildcard pattern.
   */
  while ((dp = readdir(dirp)) != NULL)
    {
      /*
       * Skip the current and parent directories.
       */
      if ((strcmp(dp->d_name, ".") == 0) ||
	  (strcmp(dp->d_name, "..") == 0))
	{
	  continue;
	}

      /*
       * If the file name doesn't match the pattern then skip it.
       */
      if (!match(dp->d_name, last))
	continue;

      /*
       * This file name is selected.
       * See if we need to reallocate the file name table.
       */
      if (fileCount >= fileTableSize)
	{
	  /*
	   * Increment the file table size and reallocate it.
	   */
	  newFileTableSize = fileTableSize + EXPAND_ALLOC;

	  newFileTable = (char **) realloc((char *) fileTable,
					   (newFileTableSize * sizeof(char *)));

	  if (newFileTable == NULL)
	    {
	      fprintf(stderr, "Cannot allocate file list\n");
	      closedir(dirp);

	      return -1;
	    }

	  fileTable = newFileTable;
	  fileTableSize = newFileTableSize;
	}

      /*
       * Allocate space for storing the file name in a chunk.
       */
      str = getChunk(dirLen + strlen(dp->d_name) + 1);

      if (str == NULL)
	{
	  fprintf(stderr, "No memory for file name\n");
	  closedir(dirp);

	  return -1;
	}

      /*
       * Save the file name in the chunk.
       */
      if (dirLen)
	memcpy(str, dirName, dirLen);

      strcpy(str + dirLen, dp->d_name);

      /*
       * Save the allocated file name into the file table.
       */
      fileTable[fileCount++] = str;
    }

  /*
   * Close the directory and check for any matches.
   */
  closedir(dirp);

  if (fileCount == 0)
    {
      fprintf(stderr, "No matches\n");

      return -1;
    }

  /*
   * Sort the list of file names.
   */
  qsort((void *) fileTable, fileCount, sizeof(char *), nameSort);

  /*
   * Return the file list and count.
   */
  *retFileTable = (const char **) fileTable;

  return fileCount;
}


/*
 * Sort routine for list of fileNames.
 */
int
nameSort(const void * p1, const void * p2)
{
  const char **	s1;
  const char **	s2;

  s1 = (const char **) p1;
  s2 = (const char **) p2;

  return strcmp(*s1, *s2);
}


/*
 * Routine to see if a text string is matched by a wildcard pattern.
 * Returns TRUE if the text is matched, or FALSE if it is not matched
 * or if the pattern is invalid.
 *  *		matches zero or more characters
 *  ?		matches a single character
 *  [abc]	matches 'a', 'b' or 'c'
 *  \c		quotes character c
 *  Adapted from code written by Ingo Wilken.
 */
BOOL
match(const char * text, const char * pattern)
{
  const char *	retryPat;
  const char *	retryText;
  int		ch;
  BOOL		found;

  retryPat = NULL;
  retryText = NULL;

  while (*text || *pattern)
    {
      ch = *pattern++;

      switch (ch)
	{
	case '*':  
	  retryPat = pattern;
	  retryText = text;
	  break;

	case '[':  
	  found = FALSE;

	  while ((ch = *pattern++) != ']')
	    {
	      if (ch == '\\')
		ch = *pattern++;

	      if (ch == '\0')
		return FALSE;

	      if (*text == ch)
		found = TRUE;
	    }

	  if (!found)
	    {
	      pattern = retryPat;
	      text = ++retryText;
	    }

	  /* fall into next case */

	case '?':  
	  if (*text++ == '\0')
	    return FALSE;

	  break;

	case '\\':  
	  ch = *pattern++;

	  if (ch == '\0')
	    return FALSE;

	  /* fall into next case */

	default:        
	  if (*text == ch)
	    {
	      if (*text)
		text++;
	      break;
	    }

	  if (*text)
	    {
	      pattern = retryPat;
	      text = ++retryText;
	      break;
	    }

	  return FALSE;
	}

      if (pattern == NULL)
	return FALSE;
    }

  return TRUE;
}


/*
 * Take a command string and break it up into an argc, argv list while
 * handling quoting and wildcards.  The returned argument list and
 * strings are in static memory, and so are overwritten on each call.
 * The argument list is ended with a NULL pointer for convenience.
 * Returns TRUE if successful, or FALSE on an error with a message
 * already output.
 */
BOOL
makeArgs(const char * cmd, int * retArgc, const char *** retArgv)
{
  const char *		argument;
  char *			cp;
  char *			cpOut;
  char *			newStrings;
  const char **		fileTable;
  const char **		newArgTable;
  int			newArgTableSize;
  int			fileCount;
  int			len;
  int			ch;
  int			quote;

  BOOL			quotedWildCards;
  BOOL			unquotedWildCards;

  static int		stringsLength;
  static char *		strings;
  static int		argCount;
  static int		argTableSize;
  static const char **	argTable;


  /*
   * Clear the returned values until we know them.
   */
  argCount = 0;
  *retArgc = 0;
  *retArgv = NULL;

  /*
   * Copy the command string into a buffer that we can modify,
   * reallocating it if necessary.
   */
  len = strlen(cmd) + 1;

  if (len > stringsLength)
    {
      newStrings = realloc(strings, len);

      if (newStrings == NULL)
	{
	  fprintf(stderr, "Cannot allocate string\n");

	  return FALSE;
	}

      strings = newStrings;
      stringsLength = len;
    }

  memcpy(strings, cmd, len);
  cp = strings;

  /*
   * Keep parsing the command string as long as there are any
   * arguments left.
   */
  while (*cp)
    {
      /*
       * Save the beginning of this argument.
       */
      argument = cp;
      cpOut = cp; 

      /*
       * Reset quoting and wildcarding for this argument.
       */
      quote = '\0';
      quotedWildCards = FALSE;
      unquotedWildCards = FALSE;

      /*
       * Loop over the string collecting the next argument while
       * looking for quoted strings or quoted characters, and
       * remembering whether there are any wildcard characters
       * in the argument.
       */
      while (*cp)
	{
	  /* Added by jean-luc.amitousa-mankoy@hotmail.fr 
	     and boris.de.finance@gmail.com
	     for upmc CL project 2013-2014 */
	  if(is_shellvar(cp)){			  
	    argument = interpret_shellvar(cp, &cp);
	    break;
	  }
	  else if(is_arithmetic_operation(cp)){
	    argument = interpret_arithmetic_operation(cp, &cp);
	  }
	  else { /* do as before */
	    ch = *cp++;
	  }
			


	  /*
	   * If we are not in a quote and we see a blank then
	   * this argument is done.
	   */
	  if (isBlank(ch) && (quote == '\0'))
	    break;

	  /*
	   * If we see a backslash then accept the next
	   * character no matter what it is.
	   */
	  if (ch == '\\')
	    {
	      ch = *cp++;

	      /*
	       * Make sure there is a next character.
	       */
	      if (ch == '\0')
		{
					
		  return FALSE;
		}

	      /*
	       * Remember whether the quoted character
	       * is a wildcard.
	       */
	      if (isWildCard(ch))
		quotedWildCards = TRUE;

	      *cpOut++ = ch;

	      continue;
	    }

	  /*
	   * If we see one of the wildcard characters then
	   * remember whether it was seen inside or outside
	   * of quotes.
	   */
	  if (isWildCard(ch))
	    {
	      if (quote)
		quotedWildCards = TRUE;
	      else
		unquotedWildCards = TRUE;
	    }

	  /*
	   * If we were in a quote and we saw the same quote
	   * character again then the quote is done.
	   */
	  if (ch == quote)
	    {
	      quote = '\0';

	      continue;
	    }

	  /*
	   * If we weren't in a quote and we see either type
	   * of quote character, then remember that we are
	   * now inside of a quote.
	   */
	  if ((quote == '\0') && ((ch == '\'') || (ch == '"')))
	    {
	      quote = ch;

	      continue;
	    }

	  /*
	   * Store the character.
	   */
	  *cpOut++ = ch;
	}

      /*
       * Make sure that quoting is terminated properly.
       */
      if (quote)
	{
	  fprintf(stderr, "Unmatched quote character\n");

	  return FALSE;
	}

      /*
       * Null terminate the argument if it had shrunk, and then
       * skip over all blanks to the next argument, nulling them
       * out too.
       */
      if (cp != cpOut)
	*cpOut = '\0';

      while (isBlank(*cp))
	*cp++ = '\0';

      /*
       * If both quoted and unquoted wildcards were used then
       * complain since we don't handle them properly.
       */
      if (quotedWildCards && unquotedWildCards)
	{
	  fprintf(stderr,
		  "Cannot use quoted and unquoted wildcards\n");

	  return FALSE;
	}

      /*
       * Expand the argument into the matching filenames or accept
       * it as is depending on whether there were any unquoted
       * wildcard characters in it.
       */
      if (unquotedWildCards)
	{
	  /*
	   * Expand the argument into the matching filenames.
	   */
	  fileCount = expandWildCards(argument, &fileTable);

	  /*
	   * Return an error if the wildcards failed to match.
	   */
	  if (fileCount < 0)
	    return FALSE;

	  if (fileCount == 0)
	    {
	      fprintf(stderr, "Wildcard expansion error\n");

	      return FALSE;
	    }
	}
      else
	{
	  /*
	   * Set up to only store the argument itself.
	   */
	  fileTable = &argument;
	  fileCount = 1;
	}

      /*
       * Now reallocate the argument table to hold the file name.
       */
      if (argCount + fileCount >= argTableSize)
	{
	  newArgTableSize = argCount + fileCount + 1;

	  newArgTable = (const char **) realloc(argTable,
						(sizeof(const char *) * newArgTableSize));

	  if (newArgTable == NULL)
	    {
	      fprintf(stderr, "No memory for arg list\n");

	      return FALSE;
	    }

	  argTable = newArgTable;
	  argTableSize = newArgTableSize;
	}

      /*
       * Copy the new arguments to the end of the old ones.
       */
      memcpy((void *) &argTable[argCount], (const void *) fileTable,
	     (sizeof(const char **) * fileCount));

      /*
       * Add to the argument count.
       */
      argCount += fileCount;
    }

  /*
   * Null terminate the argument list and return it.
   */
  argTable[argCount] = NULL;

  *retArgc = argCount;
  *retArgv = argTable;

  return TRUE;
}


/*
 * Make a NULL-terminated string out of an argc, argv pair.
 * Returns TRUE if successful, or FALSE if the string is too long,
 * with an error message given.  This does not handle spaces within
 * arguments correctly.
 */
BOOL
makeString(
	   int		argc,
	   const char **	argv,
	   char *		buf,
	   int		bufLen
	   )
{
  int	len;

  while (argc-- > 0)
    {
      len = strlen(*argv);

      if (len >= bufLen)
	{
	  fprintf(stderr, "Argument string too long\n");

	  return FALSE;
	}

      strcpy(buf, *argv++);

      buf += len;
      bufLen -= len;

      if (argc)
	*buf++ = ' ';

      bufLen--; 
    }

  *buf = '\0';

  return TRUE;
}


/*
 * Allocate a chunk of memory (like malloc).
 * The difference, though, is that the memory allocated is put on a
 * list of chunks which can be freed all at one time.  You CAN NOT free
 * an individual chunk.
 */
char *
getChunk(int size)
{
  CHUNK *	chunk;

  if (size < CHUNK_INIT_SIZE)
    size = CHUNK_INIT_SIZE;

  chunk = (CHUNK *) malloc(size + sizeof(CHUNK) - CHUNK_INIT_SIZE);

  if (chunk == NULL)
    return NULL;

  chunk->next = chunkList;
  chunkList = chunk;

  return chunk->data;
}


/*
 * Duplicate a string value using the chunk allocator.
 * The returned string cannot be individually freed, but can only be freed
 * with other strings when freeChunks is called.  Returns NULL on failure.
 */
char *
chunkstrdup(const char * str)
{
  int	len;
  char *	newStr;

  len = strlen(str) + 1;
  newStr = getChunk(len);

  if (newStr)
    memcpy(newStr, str, len);

  return newStr;
}


/*
 * Free all chunks of memory that had been allocated since the last
 * call to this routine.
 */
void
freeChunks(void)
{
  CHUNK *	chunk;

  while (chunkList)
    {
      chunk = chunkList;
      chunkList = chunk->next;
      free((char *) chunk);
    }
}


/*
 * Write all of the supplied buffer out to a file.
 * This does multiple writes as necessary.
 * Returns the amount written, or -1 on an error.
 */
int
fullWrite(int fd, const char * buf, int len)
{
  int	cc;
  int	total;

  total = 0;

  while (len > 0)
    {
      cc = write(fd, buf, len);

      if (cc < 0)
	return -1;

      buf += cc;
      total+= cc;
      len -= cc;
    }

  return total;
}


/*
 * Read all of the supplied buffer from a file.
 * This does multiple reads as necessary.
 * Returns the amount read, or -1 on an error.
 * A short read is returned on an end of file.
 */
int
fullRead(int fd, char * buf, int len)
{
  int	cc;
  int	total;

  total = 0;

  while (len > 0)
    {
      cc = read(fd, buf, len);

      if (cc < 0)
	return -1;

      if (cc == 0)
	break;

      buf += cc;
      total+= cc;
      len -= cc;
    }

  return total;
}

/* END CODE */
